/**
Copyright 2011 Steven Moxley

This file is part of Joggle.

Joggle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Joggle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Joggle.  If not, see <http://www.gnu.org/licenses/>.
*/
package edu.gwu.cs.joggle;

import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Arrays;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.io.FileWriter;

/**
 * @author      Steven Moxley <steven.moxley@gmail.com>
 * @version     0.5      
 * @since       2011-12-20
 */
public abstract class User {
	
	protected String firstName = "";
	protected String lastName = "";
	protected String userName = "";
	protected String email = "";
	
	// crypto vars
	protected PublicKey pubK;
	protected PrivateKey privK;
	protected String password;
	protected MessageDigest sha;
	
	// force DB lookups every time password is needed instead of caching it here to prevent TOCTOU attacks
	
	/**
	 * @return String This User's real first name
	 */
	protected String getFirstName() {
		return firstName;
	}
	
	/**
	 * @param fName Sets (overwrites) this User's real first name
	 */
	protected void setFirstName(String fName) {
		firstName = fName;
	}
	
	/**
	 * @return String This User's real last name
	 */
	protected String getLastName() {
		return lastName;
	}
	
	
	/**
	 * @param lName Sets (overwrites) this User's real last name
	 */
	protected void setLastName(String lName) {
		lastName = lName;
	}
	
	/**
	 * @return String This User's Joggle username
	 */
	protected String getUserName() {
		return userName;
	}
	
	/**
	 * @param uName Sets (overwites) this User's Joggle username
	 */
	protected void setUserName(String uName) {
		userName = uName;
	}
	
	/**
	 * @return String This User's preferred email address
	 */
	protected String getEmail() {
		return email;
	}
	
    /**
     * @return PublicKey This User's public key
     */
	protected PublicKey getPublicKey() {
		return pubK;
	}

	/**
	 * @param e Sets (overwrites) this User's preferred email address
	 */
	protected void setEmail(String e) {
		email = e;
	}

    /**
     * @return String returns this user's password
     */
	protected String getPassword() {
		return password;
	}

    /**
     * @param password sets this user's password
     */
	protected void setPassword(String password) {
		this.password = password;
	}

    /**
     * @return PublicKey returns this user's public key
     */
	protected PublicKey getPubK() {
		return pubK;
	}
	
    /**
     * @param m String
     * @return String
     */
	public String hash(String m) {
		byte[] msgBytes = m.getBytes();
		byte[] hash = sha.digest(msgBytes);
		return Arrays.toString(hash);
	}
	
    /**
     * @param msg String
     * @return String
     * @throws InvalidKey Exception
     * @throws NoSuchAlgorithmException
     * @throws SignatureException
     */
	protected String sign(String msg) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
		Signature sign = Signature.getInstance("SHA512WITHRSA");
		sign.initSign(privK);
		byte[] msgBytes = msg.getBytes();
		sign.update(msgBytes);   
		// have to call update() since there is no doFinal()
		byte[] signedBytes = sign.sign();
		return signedBytes.toString();
	}
					
    /**
     * @param msg String
     * @param signedMsg String
     * @returns boolean
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws SignatureException
     */
	protected boolean ver(String msg, String signedMsg) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
		Signature ver = Signature.getInstance("SHA512WITHRSA");
		ver.initVerify(pubK);
		ver.update(msg.getBytes());  
		return ver.verify(signedMsg.getBytes());
	}
	
    /**
     * Update the log file for this user with the date, username and action taken by the user. Action describes which user type action the user peformed, e.g. creating a game for a Player user or deleting a user from the database for the Administrator user.
     * @param action String
     */
	protected void updateUserLog(String action) {
		DateFormat dateformat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
		Date date = new Date();
		String logline = dateformat.format(date) + ", " + this.getUserName() + ": " + action;
		
		try{	
			FileWriter logfile = new FileWriter((ClassLoader.getSystemResource("/assets/logs/")).toString() + this.getUserName() +".txt", true);
			logfile.write(logline);
			System.out.println("Successfully wrote action to log file.\n");
		}
		catch (Exception e) {
			System.out.println("Failed to write action to log file.\n");
		}
	}


	public abstract String toString();
	
	/**
	 * @param fName The User's real first name
	 * @param lName The User's real last name
	 * @param uName The User's Joggle username
	 * @param e The User's preferred email address
	 * @throws NoSuchAlgorithmException 
	 */
	public User(String fName, String lName, String uName, String e, String pwd) {
		firstName = fName;
		lastName = lName;
		userName = uName;
		email = e;
		
		// crypto
		try {
			sha = MessageDigest.getInstance("SHA-512");
			password = hash(pwd).replaceAll("[\\[\\]]", "");
			
			// RSA setup
			KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
			gen.initialize(1024);
			KeyPair keyPair = gen.generateKeyPair();
			pubK = keyPair.getPublic();
			privK = keyPair.getPrivate();
			
		} catch (NoSuchAlgorithmException nsae) {
			nsae.printStackTrace();
		}
	}
}
